package com.kitwee.serialport;


import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

import com.kitwee.serialport.callback.SerialPortCallback;
import com.kitwee.serialport.instruction.Instruction;
import com.kitwee.serialport.data.model.SerialPortData;
import com.kitwee.serialport.data.model.SerialPortParam;
import com.kitwee.serialport.exception.SerialPortException;
import com.kitwee.serialport.service.SerialPortService;
import com.kitwee.serialport.util.Bytes;
import com.kitwee.serialport.util.LocalBroadcastManager;
import com.kitwee.serialport.util.SLog;

import java.io.File;
import java.util.ArrayList;

public final class SerialPortManager {

    @SuppressLint("StaticFieldLeak")
    private static SerialPortManager INSTANCE;

    private Context mContext;

    private SerialPortManager(Context context) {
        mContext = context.getApplicationContext();
    }

    private static final Object mLock = new Object();

    public static SerialPortManager getInstance(Context context) {
        synchronized (mLock) {
            if (INSTANCE == null) {
                INSTANCE = new SerialPortManager(context.getApplicationContext());
            }
            return INSTANCE;
        }
    }

    private SerialPortParam mParam;
    private ArrayList<Instruction> mInstructions;

    private boolean mTestMode = false;

    private SerialPortCallback mCallback;

    /**
     * 设置串口参数
     *
     * @param param 参数
     */
    public SerialPortManager setParam(SerialPortParam param) {
        this.mParam = param;
        return INSTANCE;
    }

    /**
     * 测试模式
     *
     * @param testMode 是否为测试模式
     */
    public SerialPortManager testMode(boolean testMode) {
        this.mTestMode = testMode;
        return INSTANCE;
    }

    /**
     * 设置指令集
     *
     * @param instructions 指令
     */
    public SerialPortManager setInstruction(ArrayList<Instruction> instructions) {
        this.mInstructions = instructions;
        return INSTANCE;
    }

    /**
     * 设置是否打开调试
     */
    public SerialPortManager setDebug(boolean enabled) {
        SLog.DEBUG = enabled;
        return INSTANCE;
    }

    /**
     * 注册串口数据接收器
     *
     * @param callback 回调
     */
    public SerialPortManager setCallback(SerialPortCallback callback) {
        this.mCallback = callback;
        return INSTANCE;
    }

    /**
     * 打开串口
     */
    public void open() throws SerialPortException {
        if (ensurePortAccessible()) {
            if (mTestMode) {
                SerialPortService.start(mContext, mParam, genTestInstructions());
            } else {
                if (mInstructions == null || mInstructions.isEmpty()) {
                    throw new SerialPortException("Instruction can not be empty!");
                }
                SerialPortService.start(mContext, mParam, mInstructions);
            }
            registerSerialPortReceiver();
        }
    }

    /**
     * 生成测试指令
     */
    private ArrayList<Instruction> genTestInstructions() {
        return new ArrayList<Instruction>() {{
            add(
                    new Instruction("ins1", Bytes.bytes2HexString("hello".getBytes()))
            );
            add(
                    new Instruction("ins2", Bytes.bytes2HexString(",".getBytes()))
            );
            add(
                    new Instruction("ins3", Bytes.bytes2HexString("world".getBytes()))
            );
        }};
    }

    /**
     * 确保可以访问串口
     */
    private boolean ensurePortAccessible() {
        if (!validateParam(mParam)) {
            throw new SerialPortException("Invalid param!");
        }
        File device = new File(mParam.getDevicePath());
        if (checkPermission(device)) {
            return true;
        } else {
            throw new SerialPortException("No permission!");
        }

    }

    /**
     * 检查串口参数是否有效
     *
     * @param param 串口参数
     */
    private boolean validateParam(SerialPortParam param) {
        if (param == null) {
            return false;
        }
        if (param.getDevicePath() == null) {
            SLog.e("未配置串口路径！");
            return false;
        }
        if (param.getBaudRate() == -1) {
            SLog.e("未配置波特率！");
            return false;
        }
        return true;
    }

    /**
     * 检查串口是否有读写权限
     *
     * @param device 串口设备
     */
    private boolean checkPermission(File device) throws SerialPortException {
        if (!device.canRead() || !device.canWrite()) {
            try {
                SLog.d("修改串口设备权限……");
                Process su;
                su = Runtime.getRuntime().exec("/system/bin/su");
                String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + "exit\n";
                su.getOutputStream().write(cmd.getBytes());
                if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) {
                    throw new SerialPortException("No permission!");
                }
            } catch (Exception e) {
                throw new SerialPortException("No permission!");
            }
        }
        return true;
    }

    /**
     * 串口数据广播接收器
     */
    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction() != null) {
                parseReceiverIntent(intent);
            }
        }
    };

    /**
     * 解析串口广播数据
     */
    private void parseReceiverIntent(Intent intent) {
        String action = intent.getAction();
        if (SerialPortService.ACTION_DATA_COLLECTED.equals(action)) {
            SerialPortData data = SerialPortService.parseSerialPortData(intent);
            if (data == null) {
                return;
            }
            if (mCallback != null) {
                mCallback.onSuccess(data);
            }
        } else if (SerialPortService.ACTION_SOMETHING_WRONG.equals(action)) {
            String message = SerialPortService.parseSerialPortError(intent);
            if (mCallback != null) {
                mCallback.onException(new SerialPortException(message));
            }
        }
    }

    /**
     * 注册串口数据接收器
     */
    private void registerSerialPortReceiver() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(SerialPortService.ACTION_DATA_COLLECTED);
        filter.addAction(SerialPortService.ACTION_SOMETHING_WRONG);
        LocalBroadcastManager.getInstance(mContext).registerReceiver(mReceiver, filter);
    }

    /**
     * 取消注册串口数据接收器
     */
    private void unregisterSerialPortReceiver() {
        LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mReceiver);
    }

    /**
     * 移除回调
     */
    public SerialPortManager removeCallback() {
        mCallback = null;
        return INSTANCE;
    }

    /**
     * 关闭串口
     */
    public void close() {
        unregisterSerialPortReceiver();
        SerialPortService.stop(mContext);
        mCallback = null;
    }

}
